import dataclasses
import logging.handlers
import os
from typing import Any, Optional, Dict

import pydash
from dacite import from_dict

from .config import get_config_value
from .instance import Instance


@dataclasses.dataclass
class LoggerCfg:
    name: str = ""
    filename: str = ""
    path: str = "./_log"
    level: str = "INFO"
    stdout: bool = True
    filters: str = ""


def __get_logger_cfg(name: str) -> LoggerCfg:
    """
    获取 logger 的配置数据

    Args:
        name: 日志组名称 将在 cfg.logger.name 下读取数据

    Return:
        返回读取到的配置，若无配置则返回通用配置。
    """
    dictionary = get_config_value("$.logger.{}".format(name), dataclasses.asdict(LoggerCfg()))
    if dictionary is None:
        logger_cfg = LoggerCfg()
    else:
        logger_cfg = from_dict(data_class=LoggerCfg, data=dictionary)
    if pydash.is_empty(logger_cfg.name):
        logger_cfg.name = name
    if pydash.is_empty(logger_cfg.filename):
        logger_cfg.filename = name

    return logger_cfg


def __init_logger_with_cfg(cfg: LoggerCfg):
    """ 通过配置初始化 logger

    """
    logger = logging.getLogger(cfg.name)

    # 清空当前的所有 handler
    logger.handlers.clear()
    logger.setLevel(cfg.level)

    filters = []
    for filter in pydash.split(cfg.filters, ","):
        filters.append(logging.Filter(filter))

    # 日志格式 : 时间 [等级]
    formatter = get_formatter()

    if cfg.stdout:
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        for f in filters:
            ch.addFilter(f)
        logger.addHandler(ch)

    path_ = cfg.path
    if not pydash.is_empty(path_):
        if not os.path.exists(path_):
            os.makedirs(path_)
        filename = os.path.join(path_, ".".join([cfg.filename, "log"]))
        fh = logging.handlers.RotatingFileHandler(filename=filename, mode='a', maxBytes=2 * 1024 * 1024, backupCount=5,
                                                  encoding="utf8")
        fh.setFormatter(formatter)
        for f in filters:
            fh.addFilter(f)
        logger.addHandler(fh)

    return logger


def get_formatter():
    # 修改 level 设置
    logging.addLevelName(logging.DEBUG, "DEBU")
    logging.addLevelName(logging.WARN, "WARN")
    logging.addLevelName(logging.WARNING, "WARN")
    logging.addLevelName(logging.ERROR, "ERRO")
    return logging.Formatter(fmt='%(asctime)s [%(levelname)4s] [%(name)s] %(message)s')


def __init_logger_with_name(name: str):
    """通过名称初始化一个loger

    Args:
        name: logger 主名称
    """
    cfg = __get_logger_cfg(name)
    return __init_logger_with_cfg(cfg)


def __init_logger_with_dict(dict: Dict[str, Any]):
    cfg = from_dict(data_class=LoggerCfg, data=dict)
    return __init_logger_with_cfg(cfg)


def get_logger(name: str, sub_name: str = "") -> logging.Logger:
    """
    获取一个指定名称的 Logger
    :param name: 日志消息名称
    :param sub_name: 日志消息模块名 如 "grpc.mq"
    """
    instance: Instance = Instance()
    instance.get_or_set_func(name, lambda: __init_logger_with_name(name))

    _name = name
    if not pydash.is_empty(sub_name):
        _name = ".".join([name, sub_name])

    return logging.getLogger(_name)


def get_app_logger(sub_name: str = ""):
    """
    获取一个名为 app 的 logger
    TODO: 根据项目需要可以在自己的项目内增加 logger 的获取方式
    """
    return get_logger("app", sub_name)


def get_logger_with_cfg(cfg: LoggerCfg, sub_name: str = "") -> logging.Logger:
    """
    获取一个指定名称的 Logger
    :param name: 日志消息名称
    :param sub_name: 日志消息模块名 如 "grpc.mq"
    """
    instance: Instance = Instance()
    instance.get_or_set_func(cfg.name, lambda: __init_logger_with_cfg(cfg))

    _name = cfg.name
    if not pydash.is_empty(sub_name):
        _name = ".".join([cfg.name, sub_name])

    return logging.getLogger(_name)


def get_logger_with_dict(dict: Dict[str, Any], sub_name: str = "") -> Optional[logging.Logger]:
    """
    获取一个指定名称的 Logger
    :param name: 日志消息名称
    :param sub_name: 日志消息模块名 如 "grpc.mq"
    """
    name = dict.get("name")
    if name is None:
        return None

    instance: Instance = Instance()
    instance.get_or_set_func(name, lambda: __init_logger_with_dict(dict))

    _name = name
    if not pydash.is_empty(sub_name):
        _name = ".".join([name, sub_name])

    return logging.getLogger(_name)


# region 内部logger
__LEVEL = "DEBUG"
__NAME = "internal"

# endregion
